package com.xuecheng.search.service.impl;

import com.xuecheng.api.search.model.dto.CoursePubIndexDTO;
import com.xuecheng.api.search.model.qo.QueryCoursePubIndexModel;
import com.xuecheng.common.domain.page.PageRequestParams;
import com.xuecheng.common.domain.page.PageVO;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.content.CourseGradeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.common.util.StringUtil;
import com.xuecheng.search.common.constant.ContentSearchErrorCode;
import com.xuecheng.search.service.CoursePubSearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;

/**
 * 课程搜索服务实现层(es原始Api实现)
 */
@Service
@Slf4j
public class CoursePubSearchServiceImpl implements CoursePubSearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Value("${xuecheng.elasticsearch.course.index}")
    private String indexName;

    public PageVO<CoursePubIndexDTO> queryCoursePubIndexList(PageRequestParams params, QueryCoursePubIndexModel model) {
        PageVO pageVO = null;
        try {
            //1.构建请求对象
            SearchRequest request = getRequest(params, model);

            //2.根据请求对象的数据,通过client获得响应数据
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

            //3.根据响应数据获得结果集合PageVo
            pageVO = getResponseMsg(response);
            pageVO.setPage(params.getPageNo().intValue());
            pageVO.setPageSize(params.getPageSize());

        } catch (Exception e) {
            log.error("获得课程索引数据失败: {}", e.getMessage());
            ExceptionCast.cast(ContentSearchErrorCode.E_150001);
        }
        return pageVO;
    }



    /**
     * 将响应数据封装成结果数据pageVo
     */
    private PageVO getResponseMsg(SearchResponse response) {
        //1.获得查询总条数
        SearchHits hits = response.getHits();
        long total = hits.getTotalHits().value;
        //2.获得查询结果数据集合
        SearchHit[] hitsHits = hits.getHits();

        //构建集合数据封装CoursePubIndex数据
        ArrayList<CoursePubIndexDTO> coursePubIndexDTOS = new ArrayList<>();

        //3.遍历集合封装结果内容
        for (SearchHit hitsHit : hitsHits) {
            //获得文档id值 --coursePub的id
            String id = hitsHit.getId();

            //获得cursePubIndex
            String jsonStr = hitsHit.getSourceAsString();
            CoursePubIndexDTO dto = JsonUtil.jsonToObject(jsonStr, CoursePubIndexDTO.class);

            dto.setIndexId(new Long(id));

            //获得高亮内容
            Map<String, HighlightField> highlightFields = hitsHit.getHighlightFields();

            //获得name的高亮
            HighlightField highlightField = highlightFields.get("name");
            if (!ObjectUtils.isEmpty(highlightField)) {
                Text[] fragments = highlightField.getFragments();

                StringBuffer stringBuffer = new StringBuffer();

                for (Text fragment : fragments) {
                    stringBuffer.append(fragment.toString());
                }
                String nameHighLight = stringBuffer.toString();

                dto.setName(nameHighLight);
            }
            coursePubIndexDTOS.add(dto);
        }
        //分页条件查询结果数据封装
        PageVO<CoursePubIndexDTO> pageVO = new PageVO<>();

        pageVO.setItems(coursePubIndexDTOS);
        pageVO.setCounts(total);

        return pageVO;
    }

    /**
     * 构建请求对象
     * request-->SearchSourceBuilder-->QueryBuilder
     * --> 分页
     * --> 高亮
     */
    private SearchRequest getRequest(PageRequestParams params, QueryCoursePubIndexModel model) {
        //1.创建请求对象
        SearchRequest request = new SearchRequest(indexName);
        //2.构建请求源对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //2.1设置分页数据
        Long pageNo = params.getPageNo();
        if (pageNo < 1) {
            pageNo = PageRequestParams.DEFAULT_PAGE_NUM;
            params.setPageNo(pageNo);
        }

        Integer pageSize = params.getPageSize();
        if (pageSize < 1) {
            pageSize = PageRequestParams.DEFAULT_PAGE_SIZE;
            params.setPageSize(pageSize);
        }

        int from = (pageNo.intValue() - 1) * pageSize;
        sourceBuilder.from(from);
        sourceBuilder.size(pageSize);

        //2.2设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font color='blueviolet'><b>");
        highlightBuilder.postTags("</b></font>");
        highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
        highlightBuilder.fields().add(new HighlightBuilder.Field("description"));

        sourceBuilder.highlighter(highlightBuilder);

        //3.构建查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //用户输入关键字(分词 多属性)
        String keyword = model.getKeyword();
        if (StringUtil.isNotBlank(keyword)) {
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "name", "description");
            multiMatchQueryBuilder.minimumShouldMatch("70%");
            multiMatchQueryBuilder.field("name", 10f);

            boolQueryBuilder.must(multiMatchQueryBuilder);
        }

        //课程分类(不需要分词)
        String mt = model.getMt();
        String st = model.getSt();
        if (StringUtil.isNotBlank(mt)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mt", mt));
        }
        if (StringUtil.isNotBlank(st)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("st", st));
        }
        //课程等级(不需要分词)
        String grade = model.getGrade();
        if (StringUtil.isNotBlank(grade)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("grade", grade));
        }
        //设置查询方式
        sourceBuilder.query(boolQueryBuilder);
        //4.将查询源对象设置到请求对象中
        request.source(sourceBuilder);
        return request;
    }


    /**
     * 1.判断关键数据
     * coursePubId
     * 2.根据id从索引库中获得课程发布数据
     * 3.将响应数据返回
     */
    public CoursePubIndexDTO getCoursePubIndexById(Long coursePubId) {
        CoursePubIndexDTO dto = null;
        try {
            //1.判断关键数据
            if (ObjectUtils.isEmpty(coursePubId)) {
                ExceptionCast.cast(ContentSearchErrorCode.E_150001);
            }
            //2.根据id从索引库中获得课程发布数据
            GetRequest request = new GetRequest(indexName, coursePubId.toString());
            GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);

            String jsonStr = response.getSourceAsString();
            if (StringUtil.isNotBlank(jsonStr)) {
                dto = JsonUtil.jsonToObject(jsonStr, CoursePubIndexDTO.class);

                dto.setIndexId(new Long(response.getId()));
            }
        } catch (Exception e) {
            log.error("查询课程发布数据失败: {}", e.getMessage());
            ExceptionCast.cast(ContentSearchErrorCode.E_150001);
        }
        return dto;
    }


    /**
     * 1.判断关键数据
     * coursePubId
     * 2.根据id从索引库中获得课程发布数据
     * 3.将响应数据封装并返回
     */
    public RestResponse<CoursePubIndexDTO> getCoursePubIndexById4s(Long coursePubId) {
        CoursePubIndexDTO dto = null;
        try {
            //1.判断关键数据
            if (ObjectUtils.isEmpty(coursePubId)) {
                return RestResponse.validfail(ContentSearchErrorCode.E_150001);
            }
            //2.根据id从索引库中获得课程发布数据
            GetRequest request = new GetRequest(indexName, coursePubId.toString());

            GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
            String jsonStr = response.getSourceAsString();

            //3.将响应数据封装并返回
            if (StringUtil.isNotBlank(jsonStr)) {
                dto = JsonUtil.jsonToObject(jsonStr, CoursePubIndexDTO.class);

                dto.setIndexId(new Long(response.getId()));
            }
            return RestResponse.success(dto);
        } catch (Exception e) {
            log.error("查询课程发布数据失败: {}", e.getMessage());
            return RestResponse.validfail(ContentSearchErrorCode.E_150001);
        }
    }
}